package com.ilink.teacherservice.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.common.util.concurrent.RateLimiter; // 引入Guava的RateLimiter
import com.ilink.ilinkcommon.domain.PO.studyservice.*;
import com.ilink.ilinkcommon.domain.R;
import com.ilink.teacherservice.controller.tingwu.FileHandleController;
import com.ilink.teacherservice.controller.videoTranscription.FileTranscriptionController;
import com.ilink.teacherservice.mapper.CourseSourceMapper;
import com.ilink.teacherservice.mapper.StudyCourseFileMapper;
import com.ilink.teacherservice.mapper.StudyFileSourceMapper;
import com.ilink.teacherservice.mapper.StudyResourceSortMapper;
import com.ilink.teacherservice.service.FileSourceHandleService;
import com.ilink.teacherservice.service.bailian.FileService;
import com.ilink.ilinkcommon.service.oss.OssService;
import com.ilink.ilinkcommon.utils.SnowflakeIdGenerator;
import com.ilink.teacherservice.service.FileSourceHandleThreadService;
import com.ilink.teacherservice.service.videoTranscription.FileTranscriptionService;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;


/**
 * <h3>new-ilink-java-backend</h3>
 *
 * @Description <p>用实现资源上传、加工所需的线程池服务，其中音视频加工状态获取采取的还是异步回调方式</p>
 * @Author pengshiyao
 * @Date 2024-09-16 13:46
 **/
/**
 * @ClassName FileSourceHandleThreadServiceImpl
 * @Description
 * @Author pengshiyao
 * @Date 2024-09-16 13:46
 **/
@Slf4j
@RequiredArgsConstructor
@Service
public class FileSourceHandleThreadServiceImpl implements FileSourceHandleThreadService {
    //文件转存本地路径

    @Value("${ilink.file.filestorage-path}")
    private String filestoragePath;

    @Value("${ilink.file.videostorage-path}")
    private String videostoragePath;


    //公共上传加工依赖
    private final OssService ossService;

    private final FileService fileService;
    private final FileSourceHandleService fileSourceHandleService;

    private final StudyResourceSortMapper studyResourceSortMapper;



    private final FileHandleController fileHandleController;



    //mysql引入
    private final StudyFileSourceMapper studyFileSourceMapper;
    private final CourseSourceMapper courseSourceMapper;
    private final StudyCourseFileMapper studyCourseFileMapper;


    //并发队列处理文件上传和加工
    private final ThreadPoolExecutor fileUploadTaskExecutor;
    private final ThreadPoolExecutor fileHandleTaskExecutor;

    //处理音视频的线程池
    private final ThreadPoolExecutor videoHandleTaskExecutor;

    //限流控制
    // 每分钟限制 上传任务30 次请求，可以根据需要调整速率
    private final RateLimiter UploadRateLimiter = RateLimiter.create(100);
    private final RateLimiter HandleRateLimiter = RateLimiter.create(2);
    private final RateLimiter videoRateLimiter = RateLimiter.create(10);

    //已经有RequiredArgsConstructor自动构造函数注入了，不需要再写构造函数

    // 每24小时执行一次清理任务
    @Scheduled(fixedRate = 24 * 60 * 60 * 1000)
    public void deleteLocalFiles() {
        // 定时就有选择的删除两个指定路径下的文件
        deleteLocalFilesByPath(filestoragePath);
        deleteLocalFilesByPath(videostoragePath);
    }

    private void deleteLocalFilesByPath(String path) {
        File directory = new File(path);
        if (directory.exists() && directory.isDirectory()) {
            // 获取目录下的文件列表
            File[] files = directory.listFiles();
            if (files != null) {
                List<String> deletedFiles = new ArrayList<>();
                for (File file : files) {
                    try {
                        // 获取文件属性
                        BasicFileAttributes attrs = Files.readAttributes(file.toPath(), BasicFileAttributes.class);
                        FileTime fileTime = attrs.lastModifiedTime();
                        long currentTime = System.currentTimeMillis();
                        long fileAgeInMillis = currentTime - fileTime.toMillis();

                        // 判断文件是否超过24小时
                        if (fileAgeInMillis > 24 * 60 * 60 * 1000) {
                            if (file.delete()) {
                                deletedFiles.add(file.getName());
                            }
                        }
                    } catch (Exception e) {
                        log.error("清除本地文件时，Error reading file attributes: " + e.getMessage());
                    }
                }

                // 打印删除的文件
                if (!deletedFiles.isEmpty()) {
                    log.info("Deleted files: " + deletedFiles);
                } else {
                    log.info("No files deleted.");
                }
            }
        } else {
            log.error("Directory does not exist: " + path);
        }
    }

    public  File convertMultipartFileToFile(MultipartFile multipartFile,String directoryPath,String newFilename) throws IOException {

        // 创建文件目录
        File directory = new File(directoryPath);
        if (!directory.exists()) {
            // 如果目录不存在则创建目录
            boolean dirsCreated = directory.mkdirs();
            if (!dirsCreated) {
                throw new IOException("无法创建目录: " + directoryPath);
            }
        }
        String fileName;
        // 创建本地文件名，可以使用上传的文件名，也可以使用UUID作为文件名
        if (newFilename == null || newFilename.isEmpty()) {
            fileName = multipartFile.getOriginalFilename();
        }else {
            fileName = newFilename;
        }
        // 获取上传文件的原始名称
        File file = new File(directoryPath + File.separator + fileName);  // 构建完整的文件路径

        // 将 MultipartFile 转存为 File
        multipartFile.transferTo(file);

        return file;  // 返回存储后的文件对象
    }

    //1.上传文档至OSS和百炼及转写生成加工
    @Override
    public R uploadFileTask(MultipartFile file, Long courseFileId, Long courseId, Integer sortOrder) {
        try {
            // 获取原始文件名
            String originalFilename = file.getOriginalFilename();

            // 获取文件扩展名
            String fileExtension = "";
            if (originalFilename != null && originalFilename.contains(".")) {
                fileExtension = originalFilename.substring(originalFilename.lastIndexOf("."));
            } else {
                return R.error("文件解析扩展名出现错误：" + originalFilename);
            }

            // 生成新文件名，使用雪花算法生成的ID
            SnowflakeIdGenerator idGenerator = new SnowflakeIdGenerator(1, 1);
            Long id = idGenerator.nextId();
            String newFilename = id + fileExtension;

            // 获取对应的文件路径，用于关联和存储上传
            StudyCourseFile studyCourseFile = studyCourseFileMapper.selectById(courseFileId);
            if (studyCourseFile == null) {
                return R.error("文件夹ID无效");
            }
            String filePath = studyCourseFile.getFilePath();

            // 构建并保存资源信息到数据库
            StudyFileSource courseSource = new StudyFileSource();
            courseSource.setFilesourceId(id);
            courseSource.setFileName(file.getOriginalFilename());
            courseSource.setFileId(courseFileId);
            courseSource.setCourseId(courseId);
            courseSource.setFilesourcePath(filePath + newFilename);
            boolean insertSuccess = studyFileSourceMapper.insert(courseSource)>0;
            //新存储一个顺序表

            if (insertSuccess){

                // 检查排序顺序
                if (sortOrder == null) {
                    return R.error("排序顺序不能为空");
                }
                // 创建 sort 表数据
                StudyResourceSort resourceSort = new StudyResourceSort();
                resourceSort.setSortId(idGenerator.nextId());  // 生成 sortId
                resourceSort.setFileId(courseFileId);
                resourceSort.setResourceId(id);  // courseSourceId
                resourceSort.setResourceType("word");  // 固定为 "video"
                resourceSort.setSortOrder(sortOrder + 1);  // 将传入的 sortOrder 加 1

                // 插入 sort 表
                boolean sortInsertSuccess = studyResourceSortMapper.insert(resourceSort) > 0;

                if (!sortInsertSuccess) {
                    log.error("排序信息加入mysql时失败: " + originalFilename);
                    return R.error("排序信息加入mysql时失败: " + originalFilename);
                }
                // 更新 Redis 状态为 "待处理"
//            redisTemplate.opsForHash().put("fileUploadStatus", courseSource.getFilesourceId().toString(), "待处理");
                //将文件存储在本地
                // 将文件存储在本地 改路径到配置文件
                File savedFile = convertMultipartFileToFile(file, filestoragePath, newFilename);//+filePath
                // 将上传任务提交到线程池执行
                fileUploadTaskExecutor.execute(() -> {
                    // 等待获取速率限制许可
                    UploadRateLimiter.acquire();
                    processFileUpload(savedFile, filePath, newFilename, courseSource);
                } );

                return R.ok("文件已加入队列中处理，稍后处理: " + file.getOriginalFilename());
            }else {
                log.error("文件加入mysql时失败: " + file.getOriginalFilename());
                return R.error("文件加入mysql时失败: " + file.getOriginalFilename());
            }
        } catch (Exception e) {
            //直接更改mysql中文件状态
//            StudyFileSource fileSource = studyFileSourceMapper.selectById(fileSourceId);
            return R.error("文件上传失败: " + file.getOriginalFilename(),e.getMessage());
        }
    }
    // 处理文件上传MultipartFile file
    @Override
    public void processFileUpload(File file, String filePath, String newFilename,
        StudyFileSource courseSource) {
        try {
            boolean uploadSuccess = ossService.uploadFile(file, filePath, newFilename);
            if (uploadSuccess) {
                courseSource.setOssWriteStatus(true);
                boolean updateSuccess =studyFileSourceMapper.updateById(courseSource)>0;
                if (!updateSuccess) {
                    log.error("课程:"+courseSource.getCourseId()+"中的文件:"+courseSource.getFilesourceId()+"上传成功，但更新数据库失败: " + file.getName());
                    //需要进行回退处理删除
                    boolean deleted= ossService.deleteFile(newFilename,filePath);
                    if (!deleted) {
                        log.error("课程:"+courseSource.getCourseId()+"中的文件:"+courseSource.getFilesourceId()+"上传成功，但更新数据库失败，但OSS删除失败，请手动删除: " + file.getName());
                    }else {
                        log.info("课程:" + courseSource.getCourseId() + "中的文件:" + courseSource.getFilesourceId() + "上传成功，但更新数据库失败，已进行OSS删除回退处理: " + file.getName());
                    }
                    //更新状态，即当前状态为加工失败
                    courseSource.setHandleCompleteStatus(true);
                    updateSuccess =studyFileSourceMapper.updateById(courseSource)>0;
                    if (!updateSuccess) {
                        log.error("课程:"+courseSource.getCourseId()+"中的文件:"+courseSource.getFilesourceId()+"更新数据库失败导致的加工失败，且状态未能更新: " + file.getName());
                    }
                }else {
                    log.info("课程:"+courseSource.getCourseId()+"中的文件:"+courseSource.getFilesourceId()+"上传成功: " + file.getName());
                    //将上传百炼和加工的任务提交到线程池执行，但前提是确保上传成功
                    fileHandleTaskExecutor.execute(() -> {
                        // 等待获取速率限制许可
                        HandleRateLimiter.acquire();
                        processFileHandle(file,courseSource,filePath, newFilename,courseSource.getCourseId());
                    });
                    log.info("课程:"+courseSource.getCourseId()+"中的文件:"+courseSource.getFilesourceId()+"上传成功，已提交处理任务: " + file.getName());
                }

                // 更新 Redis 状态为 "上传成功"
//                redisTemplate.opsForHash().put("fileUploadStatus", courseSource.getFilesourceId().toString(), "上传成功");
            } else {
                log.error("课程:"+courseSource.getCourseId()+"中的文件:"+courseSource.getFilesourceId()+"上传失败: " + file.getName());
                //更新状态，即当前状态为加工失败
                courseSource.setHandleCompleteStatus(true);
                boolean updateSuccess =studyFileSourceMapper.updateById(courseSource)>0;
                if (!updateSuccess) {
                    log.error("课程:"+courseSource.getCourseId()+"中的文件:"+courseSource.getFilesourceId()+"上传失败，且加工状态更新数据库失败: " + file.getName());
                }
                // 更新 Redis 状态为 "上传失败"
//                redisTemplate.opsForHash().put("fileUploadStatus", courseSource.getFilesourceId().toString(), "上传失败");
            }
        } catch (Exception e) {
            // 更新 Redis 状态为 "上传失败"
//            redisTemplate.opsForHash().put("fileUploadStatus", courseSource.getFilesourceId().toString(), "上传失败");
            log.error("课程:"+courseSource.getCourseId()+"中的文件:"+courseSource.getFilesourceId()+"上传中出现异常: " + file.getName(), e);
            //更新状态，即当前状态为加工失败
            courseSource.setHandleCompleteStatus(true);
            boolean updateSuccess =studyFileSourceMapper.updateById(courseSource)>0;
            if (!updateSuccess) {
                log.error("课程:"+courseSource.getCourseId()+"中的文件:"+courseSource.getFilesourceId()+"上传失败，且加工状态更新数据库失败: " + file.getName());
            }
        }
    }



    //RAG加工和百炼上传加工MultipartFile file,
    @Override
    public void processFileHandle(File file, StudyFileSource fileSource, String filePath,
        String newFilename, Long courseId) {
        try {
            String fileUrl = ossService.getFileUrl(fileSource.getFilesourcePath());
            if (fileUrl == null) {
                log.error("课程:" + courseId + "中的文件URL未找到: " + fileSource.getFileName());
            }
            //RAG上传加工
//            String tempFilePath = "tempFile_rag" + fileSource.getFilesourceId() + ".tmp";
//            MultipartFile file= downloadFile1(fileUrl, tempFilePath);
//            File file = new File(tempFilePath);
//            MultipartFile file = getMultipartFileFromUrl(fileUrl,tempFilePath);
            R response = fileService.uploadFile(file, filePath, newFilename);
            if (response == null || response.getCode() == 0) {
                if (response != null) {
                    fileSource.setHandleCompleteStatus(true);
                    studyFileSourceMapper.updateById(fileSource);
                    log.error("课程:" + courseId + "中的RAG文件:"+fileSource.getFilesourceId()+"上传失败: " + fileSource.getFileName() + " " + response.getMsg());

                } else {
                    fileSource.setHandleCompleteStatus(true);
                    studyFileSourceMapper.updateById(fileSource);
                    log.error("课程:" + courseId + "中的RAG文件:"+fileSource.getFilesourceId()+"上传失败: " + fileSource.getFileName());
                }
            } else {
                fileSource.setRagCompleteStatus(true);
                studyFileSourceMapper.updateById(fileSource);
            }
            response = fileHandleController.uploadFileFromUrl(fileUrl, fileSource.getFilesourceId());
            if (response == null || response.getCode() == 0) {
                if (response != null) {
                    fileSource.setHandleCompleteStatus(true);
                    studyFileSourceMapper.updateById(fileSource);
                    log.info("nihao4");
                    log.error("课程:" + courseId + "中的文件:"+fileSource.getFilesourceId()+"加工失败: " + fileSource.getFileName() + " " + response.getMsg());
                } else {
                    fileSource.setHandleCompleteStatus(true);
                    studyFileSourceMapper.updateById(fileSource);
                    log.info("nihao5");
                    log.error("课程:" + courseId + "中的文件:"+fileSource.getFilesourceId()+"加工失败: " + fileSource.getFileName());
                }
            } else {
                fileSource.setSmartTextStatus(true);
                fileSource.setHandleCompleteStatus(true);
                studyFileSourceMapper.updateById(fileSource);
//                return R.ok("文件加工转写成功: " +  fileSource.getFileName(),response);
                log.info("nihao6");
                log.info("课程:" + courseId + "中的文件:"+fileSource.getFilesourceId()+"加工转写成功: " + fileSource.getFileName());
            }

        } catch (Exception e) {
            log.error("课程:" + courseId + "中的文件:"+fileSource.getFilesourceId()+"上传中出现异常: " , e);
            //更新状态，即当前状态为加工失败
            fileSource.setHandleCompleteStatus(true);
            boolean updateSuccess =studyFileSourceMapper.updateById(fileSource)>0;
            if (!updateSuccess) {
                log.error("课程:"+fileSource.getCourseId()+"中的文件:"+fileSource.getFilesourceId()+"上传失败，且加工状态更新数据库失败: " );
            }
        }
    }


    //2.上传音视频
    @Override
    public R uploadSourceTask(MultipartFile file, Long courseFileId, String language,
        Boolean isTranslated, String speakerType, Long courseid, Integer sortOrder) {
        try {
            // 获取原始文件名
            String originalFilename = file.getOriginalFilename();

            // 获取文件扩展名
            String fileExtension = "";
            if (originalFilename != null && originalFilename.contains(".")) {
                fileExtension = originalFilename.substring(originalFilename.lastIndexOf("."));
            }else {
                return R.error("文件解析扩展名出现错误："+originalFilename);
            }
            // 生成新文件名，使用雪花算法生成的ID
            SnowflakeIdGenerator idGenerator = new SnowflakeIdGenerator(1, 1);
            Long id = idGenerator.nextId();
            String newFilename = id+ fileExtension;
            // 获取对应的文件路径，用于关联和存储上传
            StudyCourseFile studyCourseFile = studyCourseFileMapper.selectById(courseFileId);
            if (studyCourseFile == null) {
                return R.error("文件夹ID无效");
            }
            String filePath = studyCourseFile.getFilePath();
            CourseSource courseSource = new CourseSource();
            courseSource.setCourseSourceId(id);
            courseSource.setLanguage(language);
            courseSource.setIsTranslated(isTranslated);
            courseSource.setSpeakerType(speakerType);
            courseSource.setFileId(courseFileId);
            courseSource.setFileName(file.getOriginalFilename());
            courseSource.setCourseId(courseid);
            courseSource.setOssWriteStatus(false);
            courseSource.setTranscriptionComplete(false);
            courseSource.setTranscriptionCompleteStatus(false);
//            courseSource.se   tTaskId("");
            // 构建并保存资源信息到数据库
            courseSource.setFilesourcePath(filePath+newFilename);
            boolean insertSuccess = courseSourceMapper.insert(courseSource)>0;

            if (insertSuccess){
                // 检查排序顺序
                if (sortOrder == null) {
                    return R.error("排序顺序不能为空");
                }
                // 创建 sort 表数据
                StudyResourceSort resourceSort = new StudyResourceSort();
                resourceSort.setSortId(idGenerator.nextId());  // 生成 sortId
                resourceSort.setFileId(courseFileId);
                resourceSort.setResourceId(id);  // courseSourceId
                resourceSort.setResourceType("video");  // 固定为 "video"
                resourceSort.setSortOrder(sortOrder + 1);  // 将传入的 sortOrder 加 1

                // 插入 sort 表
                boolean sortInsertSuccess = studyResourceSortMapper.insert(resourceSort) > 0;

                if (!sortInsertSuccess) {
                    log.error("排序信息加入mysql时失败: " + originalFilename);
                    return R.error("排序信息加入mysql时失败: " + originalFilename);
                }
                //将文件存储在本地 改路径到配置文件
                File savedFile = convertMultipartFileToFile(file, videostoragePath, newFilename);//+filePath


                // 将上传任务提交到线程池执行
                fileUploadTaskExecutor.execute(() -> {      // 等待获取速率限制许可
                    UploadRateLimiter.acquire();
                    processSourceUpload(savedFile, filePath, newFilename, courseSource);
                });

                return R.ok("音视频文件已加入队列中处理，稍后处理: " + file.getOriginalFilename());
            }else {
                log.error("音视频文件加入mysql时失败: " + file.getOriginalFilename());
                return R.error("音视频文件加入mysql时失败: " + file.getOriginalFilename());
            }
        }
        catch (Exception e) {
            log.error("音视频文件上传时出现异常: " + file.getOriginalFilename(), e);
            return R.error("音视频文件上传时出现异常: " + file.getOriginalFilename(),e.getMessage());
        }

    }

    //处理音视频上传和加工
    @Override
    public void processSourceUpload(File file, String filePath, String newFilename,
        CourseSource courseSource) {
        try {
            boolean uploadSuccess = ossService.uploadFile(file, filePath, newFilename);
            if (uploadSuccess) {
                courseSource.setOssWriteStatus(true);
                boolean updateSuccess =courseSourceMapper.updateById(courseSource)>0;
                if (!updateSuccess) {
                    log.error("课程:"+courseSource.getCourseId()+"中的音视频上传成功，但更新数据库失败: " + file.getName());
                    //需要进行回退处理删除
                    ossService.deleteFile(newFilename,filePath);
                    log.info("课程:"+courseSource.getCourseId()+"中的音视频上传成功，但更新数据库失败，已进行OSS删除回退处理: " + file.getName());
                }else {
                    log.info("课程:"+courseSource.getCourseId()+"中的音视频上传成功: " + file.getName());
                    String fileUrl = ossService.getFileUrl(courseSource.getFilesourcePath());
                    if(fileUrl == null){
                        log.error("课程:"+courseSource.getCourseId()+"中的音视频上传成功，但获取文件URL失败: " + file.getName());
                    }
                    //将加工的任务提交到线程池执行，但前提是确保上传成功
                    videoHandleTaskExecutor.execute(() ->
                    {// 等待获取速率限制许可
                        videoRateLimiter.acquire();
                        try {
                            log.info("fileSourceHandleService: {}", fileSourceHandleService);
                            log.info("courseSource: {}", courseSource);
                            log.info("fileUrl: {}", fileUrl);

                            fileSourceHandleService.handlefileTrans(courseSource,fileUrl);
                        }
                        catch (Exception e) {
                            log.error("课程:"+courseSource.getCourseId()+"中的音视频加工加入队列时 出现异常: " + file.getName(), e);
                            courseSource.setTranscriptionCompleteStatus(true);
                            log.info("用于更新的数据:"+courseSource);
                            boolean setStatus=courseSourceMapper.updateById(courseSource)>0;
                            if (!setStatus) {
                                log.error("课程:"+courseSource.getCourseId()+"中的音视频加工加入队列出现异常，且最后更新数据库失败: " + file.getName());
                            }

                        }
                    });
                    //加工音视频，启用异步线程用于轮询加工状态直到加工完成，但这虽然默认使用线程池排队但这会导致线程闲置因此，需要对此优化
                    //studyaboutTeacherService.checkVideoStatusAsync(courseSource.getCourseSourceId());
                    //先加工然后再检查状态
                    fileSourceHandleService.checkVideoStatusAsync(courseSource.getCourseSourceId());
                    log.info("课程:"+courseSource.getCourseId()+"中的音视频上传成功，已提交异步处理任务: " + file.getName());
                }

            } else {
                log.error("课程:"+courseSource.getCourseId()+"中的音视频上传失败: " + file.getName());

            }
        } catch (Exception e) {
            log.error("课程:"+courseSource.getCourseId()+"中的音视频上传中出现异常: " + file.getName(), e);
        }
    }
    @Override
    public Map<String, Boolean> checkRateLimiterStatus() {
        // 检查三个限流器的状态
        boolean isUploadAvailable = UploadRateLimiter.tryAcquire();
        boolean isHandleAvailable = HandleRateLimiter.tryAcquire();
        boolean isVideoAvailable = videoRateLimiter.tryAcquire();

        // 将限流器的状态存入 Map
        Map<String, Boolean> rateLimiterStatus = new HashMap<>();
        rateLimiterStatus.put("UploadRateLimiter", isUploadAvailable);
        rateLimiterStatus.put("HandleRateLimiter", isHandleAvailable);
        rateLimiterStatus.put("VideoRateLimiter", isVideoAvailable);

        return rateLimiterStatus;
    }

}
